library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
use work.alu_type.all;

entity dlx_cu_pipe is
port (	Clk				: in   std_logic;  -- Clock
		Rst				: in   std_logic;  -- Reset:Active-Low
		-- Instruction Register
		IR_IN			: in   std_logic_vector(31 downto 0);
		-- IF Control Signal
		IR_LATCH_EN		: out std_logic;  -- Instruction Register Latch Enable
		NPC_LATCH_EN	: out std_logic;  -- NextProgramCounter Register Latch Enable
		-- ID Control Signals
		RegA_LATCH_EN	: out std_logic;  -- Register A Latch Enable
		RegB_LATCH_EN      : out std_logic;  -- Register B Latch Enable
		RegIMM_LATCH_EN	: out std_logic;  -- Immediate Register Latch Enable
		-- EX Control Signal
		EQ_COND			: out std_logic;  -- Branch if (not) Equal to Zero
		JUMP_EN			: out std_logic;  -- JUMP Enable Signal for PC input MUX
		ALU_OPCODE		: out TYPE_OP; 
									  -- ALU Operation Code
		MUXA_SEL		: out std_logic;  -- MUX-A Sel
		MUXB_SEL		: out std_logic;  -- MUX-B Sel
		ALU_OUTREG_EN	: out std_logic;  -- ALU Output Register Enable
		-- MEM Control Signals
		DRAM_WE		: out std_logic;  -- Data RAM Write Enable
		LMD_LATCH_EN	: out std_logic;  -- LMD Register Latch Enable
		PC_LATCH_EN		: out std_logic;  -- Program Counte Latch Enable
		WB_MUX_SEL		: out std_logic;  -- Write Back MUX Sel
		RF_WE			: out std_logic   -- Register File Write Enable
	);
end dlx_cu_pipe;

architecture dlx_cu_rtl of dlx_cu_pipe is

constant MICROCODE_MEM_SIZE	:     integer := 64;  -- Microcode Memory Size
constant FUNC_SIZE			:     integer := 11;  -- Func Field Size for R-Type Ops
constant OP_CODE_SIZE			:     integer := 6;  -- Op Code Size
constant ALU_OPC_SIZE			:     integer := 2;  -- ALU Op Code Word Size
constant IR_SIZE				:     integer := 32;  -- Instruction Register Size    
constant CW_SIZE				:     integer := 15;  -- Control Word Size
 
constant R_OPCODE : std_logic_vector(OP_CODE_SIZE -1 downto 0) := "000000";

  type mem_array is array (integer range 0 to MICROCODE_MEM_SIZE-1) of std_logic_vector(CW_SIZE - 1 downto 0);

  signal cw_mem : mem_array := (
				"111101001000011",	--reg-to-reg instruction
				"000000000000000",
				"111010111100111",	--jump (0x02)
				"111011111100111",	--jal (0x03)
				"111010111100111",	--beqz (0x04)
				"111010111000111",	--bnez (0x05)
				"000000000000000",
				"000000000000000",
				"111011011000011",	--addi (0x08)
				"111011011000011",	--addui (0x09)
				"111011011000011",	--subi (0x0a)
				"111011011000011",	--subui (0x0b)
				"111011011000011",	--andi (0x0c)
				"111011011000011",	--ori (0x0d)
				"111011011000011",	--xori (0x0e)
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"111010001100111",	--jr (0x12)
				"111011001100111",	--jalr (0x13)
				"111011011000011",	--slli (0x14)
				"110000000000001",	--nop (0x15)
				"111011011000011",	--srli (0x16)
				"111011011000011",	--srai (0x17)
				"111011011000011",	--seqi (0x18)
				"111011011000011",	--snei (0x19)
				"111011011000011",	--slti (0x1a)
				"111011011000011",	--sgti (0x1b)
				"111011011000011",	--slei (0x1c)
				"111011011000011",	--sgei (0x1d)
				"000000000000000",	
				"000000000000000",	
				"111011011001001",	--lb (0x20)			--32
				"111011011001001",	--lh (0x21)
				"000000000000000",	
				"111011011001001",	--lw (0x23)
				"111011011001001",	--lbu (0x24)
				"111011011001001",	--lhu (0x25)
				"000000000000000",	
				"000000000000000",		
				"111110011011001",	--sb (0x28)
				"111110011011001",	--sh (0x29)
				"000000000000000",
				"111110011011001",	--sw (0x2b)
				"000000000000000",
				"000000000000000",
				"000000000000000",
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"000000000000000",	
				"111011011000011",	--sltui (0x3a)
				"111011011000011",	--sgtui (0x3b)
				"111011011000011",	--sleui (0x3c)
				"111011011000011",	--sgeui (0x3d)
				"000000000000000",
				"000000000000000"
			);
  signal IR_i : std_logic_vector(16 downto 0);  -- OpCode+Func
  signal cw   : std_logic_vector(CW_SIZE - 1 downto 0);

  signal cw1 : std_logic_vector(CW_SIZE - 1 downto 0);
  signal cw2 : std_logic_vector(CW_SIZE - 1 - 2 downto 0);
  signal cw3 : std_logic_vector(CW_SIZE - 1 - 5 downto 0);
  signal cw4 : std_logic_vector(CW_SIZE - 1 - 9 downto 0);
  signal cw5 : std_logic_vector(CW_SIZE - 1 - 13 downto 0);

  signal ALU_OPCODE_i : TYPE_OP;
  signal ALU_OPCODE1 : TYPE_OP;
  signal ALU_OPCODE2 : TYPE_OP;
  signal ALU_OPCODE3 : TYPE_OP;

begin

	IR_i(16 downto 11)	<= IR_IN(IR_SIZE -1 downto 26);		--opcode
	IR_i(10 downto 0)	<= IR_IN(FUNC_SIZE - 1 downto 0);	--func

	cw <= cw_mem(to_integer(unsigned(IR_i(16 downto 11)))); --2^6=64 max lines

	--instruction fetch
	IR_LATCH_EN		<= cw1(14); --(CW_SIZE - 1);
	NPC_LATCH_EN	<= cw1(13); --(CW_SIZE - 2);

	--instruction decode
	RegA_LATCH_EN	<= cw2(12); --(CW_SIZE - 3);
	RegB_LATCH_EN	<= cw2(11); --(CW_SIZE - 4);
	RegIMM_LATCH_EN	<= cw2(10); --(CW_SIZE - 5);
	
	--execution stage
	MUXA_SEL		<= cw3(8); --(CW_SIZE - 7);
	MUXB_SEL		<= cw3(7); --(CW_SIZE - 8);
	ALU_OUTREG_EN	<= cw3(6); --(CW_SIZE - 9);
	EQ_COND			<= cw3(5); --(CW_SIZE - 10);
	
	--memory stage
	DRAM_WE		<= cw4(4);--(CW_SIZE - 11);
	LMD_LATCH_EN	<= cw4(3);--(CW_SIZE - 12);
	JUMP_EN			<= cw4(2);--(CW_SIZE - 13);
	PC_LATCH_EN		<= cw4(0);--(CW_SIZE - 15);
	--PC_LATCH_EN		<= '1';--(CW_SIZE - 15);
	
	--write back
	RF_WE			<= cw5(1); --(CW_SIZE - 6);
	WB_MUX_SEL		<= cw5(0); --(CW_SIZE - 14);
	
	CW_PIPE: process (Clk, Rst)
	begin  -- process Clk
		if Rst = '1' then                   -- asynchronous reset (active high)
			cw1 <= (others => '0');
			cw2 <= (others => '0');
			cw3 <= (others => '0');
			cw4 <= (others => '0');
			cw5 <= (others => '0');
			ALU_OPCODE1 <= NOP;
			ALU_OPCODE2 <= NOP;
			ALU_OPCODE3 <= NOP;
		elsif rising_edge(Clk) then  -- rising clock edge
			cw1 <= cw;
			cw2 <= cw1(12 downto 0);	--(CW_SIZE - 1 - 2 downto 0);
			cw3 <= cw2(9 downto 0);	--(CW_SIZE - 1 - 5 downto 0);
			cw4 <= cw3(9) & cw3(4 downto 0);--(CW_SIZE - 1 - 9 downto 0);
			cw5 <= cw4(5) & cw4(1);	--(CW_SIZE - 1 - 13 downto 0);
			
			ALU_OPCODE1 <= ALU_OPCODE_i;
			ALU_OPCODE2 <= ALU_OPCODE1;
			ALU_OPCODE3 <= ALU_OPCODE2;
		end if;
	end process CW_PIPE;
	
	ALU_OPCODE <= ALU_OPCODE3;

ALU_OP_CODE_P: process (IR_i,IR_IN)
begin  -- process ALU_OP_CODE_P

	case IR_i(16 downto 11) is
		when R_OPCODE =>         -- R-Type Instructions
			case to_integer(unsigned(IR_i(10 downto 0))) is
				when 4 => -- SLL
					ALU_OPCODE_i <= LLSH;
				when 6 => -- SRL
					ALU_OPCODE_i <= LRSH;
				when 7 => -- SRA
					ALU_OPCODE_i <= ARSH;
				when 32 => -- ADD
					ALU_OPCODE_i <= ADDS;
				when 33=>  -- ADDU
					ALU_OPCODE_i <= ADDU;
				when 34 => -- SUB
					ALU_OPCODE_i <= SUBS;        
				when 35 => -- SUBU
					ALU_OPCODE_i <= SUBS; 
				when 36 => --AND
					ALU_OPCODE_i <= BIT_AND;	
				when 37 => --OR
					ALU_OPCODE_i <= BIT_OR;	
				when 38 => --XOR
					ALU_OPCODE_i <= BIT_XOR;
				when 40 => --SEQ
					ALU_OPCODE_i <= EQUALS;
				when 41 => --SNE
					ALU_OPCODE_i <= NOT_EQ;
				when 42 => --SLT
					ALU_OPCODE_i <= LESS;
				when 43 => --SGT
					ALU_OPCODE_i <= GREATER;
				when 44 => --SLE
					ALU_OPCODE_i <= LE_EQ;
				when 45 => --SGE
					ALU_OPCODE_i <= GR_EQ;
				when 58 => --SLTU
					ALU_OPCODE_i <= LESS_U;
				when 59 => --SGTU
					ALU_OPCODE_i <= GREATER_U;
				when 60 => --SLEU
					ALU_OPCODE_i <= LE_EQ_U;
				when 61 => --SGEU
					ALU_OPCODE_i <= GR_EQ_U;
				when others =>
					ALU_OPCODE_i <= NOP;
			end case;
		when "000010" => --J
			ALU_OPCODE_i <=ADDS;
		when "000011" => --JAL
			ALU_OPCODE_i <=ADDS;
		when "000100" => --BEQZ
			ALU_OPCODE_i <=ADDS;
		when "000101" => --BNEZ
			ALU_OPCODE_i <=ADDS;
		when "001000" => --ADDI
			ALU_OPCODE_i <= ADDS;
		when "001001" => --ADDUI
			ALU_OPCODE_i <= ADDU;
		when "001010" => --SUBI
			ALU_OPCODE_i <= SUBS;
		when "001011" => --SUBUI
			ALU_OPCODE_i <= SUBU;				
		when "001100" => --ANDI
			ALU_OPCODE_i <= BIT_AND;	
		when "001101" => --ORI
			ALU_OPCODE_i <= BIT_OR;	
		when "001110" => --XORI
			ALU_OPCODE_i <= BIT_XOR;	
		when "010010" => --JR
			ALU_OPCODE_i <= MOV;	
		when "010011" => --JALR
			ALU_OPCODE_i <= MOV;	
		when "010100" => --SLLI
			ALU_OPCODE_i <= LLSH;
		when "010101" => --NOP
			ALU_OPCODE_i <= NOP;
		when "010110" => --SRLI
			ALU_OPCODE_i <= LRSH;
		when "010111" => --SRAI
			ALU_OPCODE_i <= ARSH;
		when "011000" => --SEQI
			ALU_OPCODE_i <= EQUALS;
		when "011001" => --SNEI
			ALU_OPCODE_i <= NOT_EQ;
		when "011010" => --SLTI
			ALU_OPCODE_i <= LESS;
		when "011011" => --SGTI
			ALU_OPCODE_i <= GREATER;
		when "011100" => --SLEI
			ALU_OPCODE_i <= LE_EQ;
		when "011101" => --SGEI
			ALU_OPCODE_i <= GR_EQ;
		when "100000" => --LB
			ALU_OPCODE_i <= ADDS;
		when "100001" => --LH
			ALU_OPCODE_i <= ADDS;
		when "100011" => --LW
			ALU_OPCODE_i <= ADDS;
		when "100100" => --LBU
			ALU_OPCODE_i <= ADDS;
		when "100101" => --LHU
			ALU_OPCODE_i <= ADDS;
		when "101000" => --SB
			ALU_OPCODE_i <= ADDS;
		when "101001" => --SH
			ALU_OPCODE_i <= ADDS;
		when "101011" => --SW
			ALU_OPCODE_i <= ADDS;
		when "111010" => --SLTUI
			ALU_OPCODE_i <= LESS_U;
		when "111011" => --SGTUI
			ALU_OPCODE_i <= GREATER_U;
		when "111100" => --SLEUI
			ALU_OPCODE_i <= LE_EQ_U;
		when "111101" => --SGEUI
			ALU_OPCODE_i <= GR_EQ_U;
		when others => 
			ALU_OPCODE_i <= NOP;
	end case;
	
end process ALU_OP_CODE_P;


end architecture dlx_cu_rtl;
